"""
区块链配置类
"""

import os
import sys
from typing import Optional, Dict, Any
from pydantic import BaseSettings, Field
from loguru import logger
from pathlib import Path


class BlockchainConfig(BaseSettings):
    """
    区块链配置类
    用于管理FISCO BCOS连接配置
    """
    
    # 项目根目录配置
    project_root: str = Field(default=".", description="项目根目录路径")
    
    # SDK路径配置
    python_sdk_path: str = Field(default="./python-sdk", description="Python SDK路径")
    
    # 网络配置
    channel_host: str = Field(default="127.0.0.1", description="节点主机地址")
    channel_port: int = Field(default=20200, description="节点端口")
    group_id: int = Field(default=1, description="群组ID")
    chain_id: int = Field(default=1, description="链ID")
    
    # 加密配置
    crypto_type: str = Field(default="ECDSA", description="加密类型")
    use_sm_crypto: bool = Field(default=False, description="是否使用国密")
    
    # 证书配置
    channel_ca: str = Field(default="bin/ca.crt", description="CA证书路径")
    channel_node_cert: str = Field(default="bin/sdk.crt", description="节点证书路径")
    channel_node_key: str = Field(default="bin/sdk.key", description="节点私钥路径")
    channel_en_crt: str = Field(default="bin/gmensdk.crt", description="国密加密证书路径")
    channel_en_key: str = Field(default="bin/gmensdk.key", description="国密加密私钥路径")
    
    # 账户配置
    account_keyfile_path: str = Field(default="bin/accounts", description="账户文件路径")
    account_keyfile: str = Field(default="pemtest.pem", description="账户文件名")
    account_password: str = Field(default="", description="账户密码")
    gm_account_keyfile: str = Field(default="gm_account.json", description="国密账户文件")
    gm_account_password: str = Field(default="", description="国密账户密码")
    hex_private_key: Optional[str] = Field(default=None, description="十六进制私钥")
    
    # 合约配置
    contract_name: str = Field(default="HelloWorld", description="合约名称")
    contract_address: Optional[str] = Field(default=None, description="合约地址")
    abi_path: str = Field(default="resources/abi/HelloWorld.abi", description="ABI文件路径")
    bin_path: str = Field(default="resources/bin/ecc/HelloWorld.bin", description="BIN文件路径")
    
    # 资源目录配置
    resources_dir: str = Field(default="resources", description="资源根目录")
    abi_dir: str = Field(default="resources/abi", description="ABI文件目录")
    bin_dir: str = Field(default="resources/bin/ecc", description="BIN文件目录")
    
    # 编译器配置
    solc_path: str = Field(default="bin/solc/solc.exe", description="Solidity编译器路径")
    gm_solc_path: str = Field(default="./bin/solc/solc-gm.exe", description="国密Solidity编译器路径")
    
    # 日志配置
    log_dir: str = Field(default="bin/logs", description="日志目录")
    
    # 合约相关路径
    contract_dir: str = Field(default="./contracts", description="合约目录")
    contract_info_file: str = Field(default="bin/contract.ini", description="合约信息文件")
    
    # 客户端协议配置
    client_protocol: str = Field(default="channel", description="客户端协议类型")
    remote_rpc_url: str = Field(default="", description="远程RPC URL")
    
    # FISCO BCOS 3.0 配置
    bcos3_lib_path: str = Field(default="./bcos3sdklib", description="BCOS3库路径")
    bcos3_config_file: str = Field(default="./bcos3sdklib/bcos3_sdk_config.ini", description="BCOS3配置文件")
    bcos3_group: str = Field(default="group0", description="BCOS3群组")
    bcos3_check_node_version: bool = Field(default=True, description="是否检查节点版本")
    bcos3_when_version_mismatch: str = Field(default="WARN", description="版本不匹配时的处理方式")
    bcos3_major_version: int = Field(default=3, description="BCOS主版本号")
    bcos3_max_miner_version: int = Field(default=6, description="最大子版本号")
    
    class Config:
        case_sensitive = False
        env_file = Path(__file__).parent.parent.parent / ".env"
        env_file_encoding = "utf-8"
        env_prefix = "BLOCKCHAIN_"
        
    def __init__(self):
        """
        初始化配置
        """
        # 先调用父类初始化
        super().__init__()
        
        # 动态添加python-sdk路径到系统路径
        self._add_python_sdk_path()
        
        logger.info(f"区块链配置初始化完成，连接节点: {self.channel_host}:{self.channel_port}")
    
    def _add_python_sdk_path(self):
        """
        添加python-sdk路径到系统路径
        """
        try:
            sdk_path = self._resolve_path(self.python_sdk_path)
            
            # 检查路径是否存在
            if not sdk_path.exists():
                logger.warning(f"Python SDK路径不存在: {sdk_path}")
                return
            
            # 添加到系统路径（如果还没有添加）
            sdk_path_str = str(sdk_path)
            if sdk_path_str not in sys.path:
                sys.path.insert(0, sdk_path_str)
                logger.debug(f"已添加Python SDK路径到系统路径: {sdk_path_str}")
            
        except Exception as e:
            logger.error(f"添加Python SDK路径失败: {e}")
    
    @property
    def peers(self) -> str:
        """
        获取节点地址字符串
        
        Returns:
            节点地址字符串
        """
        return f"{self.channel_host}:{self.channel_port}"
    
    @property
    def abi_filename(self) -> str:
        """从abi_path提取文件名"""
        return Path(self.abi_path).name
    
    @property
    def bin_filename(self) -> str:
        """从bin_path提取文件名"""
        return Path(self.bin_path).name
    
    def get_contract_config(self) -> Dict[str, Any]:
        """
        获取合约配置字典
        
        Returns:
            合约配置字典
        """
        return {
            'name': self.contract_name,
            'address': self.contract_address,
            'abi_filename': self.abi_filename,
            'bin_filename': self.bin_filename
        }
    
    def _resolve_path(self, path_str: str) -> Path:
        """
        解析路径，相对路径基于项目根目录解析
        
        Args:
            path_str: 路径字符串
            
        Returns:
            解析后的路径
        """
        path = Path(path_str)
        if path.is_absolute():
            return path.resolve()
        else:
            # 相对路径基于配置的项目根目录
            return (Path(self.project_root) / path).resolve()
    
    def get_abi_content(self) -> str:
        """
        获取ABI文件内容
        
        Returns:
            ABI文件内容
        """
        try:
            return self._resolve_path(self.abi_path).read_text(encoding='utf-8')
        except Exception as e:
            logger.error(f"读取ABI文件失败: {e}")
            raise
    
    def get_bin_content(self) -> str:
        """
        获取BIN文件内容
        
        Returns:
            BIN文件内容
        """
        try:
            return self._resolve_path(self.bin_path).read_text(encoding='utf-8')
        except Exception as e:
            logger.error(f"读取BIN文件失败: {e}")
            raise
    
    def get_resource_path(self, resource_type: str, filename: str) -> Path:
        """
        获取资源文件路径
        
        Args:
            resource_type: 资源类型 ('abi', 'bin', 'cert')
            filename: 文件名
            
        Returns:
            资源文件路径
        """
        try:
            if resource_type == 'abi':
                # 使用环境变量配置的 abi_dir
                return self._resolve_path(f"{self.abi_dir}/{filename}")
            elif resource_type == 'bin':
                # 使用环境变量配置的 bin_dir
                return self._resolve_path(f"{self.bin_dir}/{filename}")
            elif resource_type == 'cert':
                return self._resolve_path(filename)
            else:
                raise ValueError(f"不支持的资源类型: {resource_type}")
        except Exception as e:
            logger.error(f"获取资源路径失败: {e}")
            raise
    
    def get_path(self, path_config: str, fallback: str = None) -> str:
        """
        通用路径获取方法
        
        Args:
            path_config: 配置路径
            fallback: 失败时的回退值
            
        Returns:
            解析后的绝对路径
        """
        try:
            return str(self._resolve_path(path_config))
        except Exception as e:
            logger.error(f"获取路径失败: {e}")
            return fallback or path_config
    
    def get_log_dir_path(self) -> str:
        """获取日志目录路径"""
        return self.get_path(self.log_dir)
    
    def get_cert_path(self, cert_filename: str) -> str:
        """获取证书文件路径"""
        return self.get_path(cert_filename)
    
    def get_account_dir_path(self) -> str:
        """获取账户目录的绝对路径"""
        return self.get_path(self.account_keyfile_path)
    
    def get_account_keyfile_path(self) -> str:
        """获取账户密钥文件路径"""
        try:
            account_dir = Path(self.get_account_dir_path())
            return str(account_dir / self.account_keyfile)
        except Exception as e:
            logger.error(f"获取账户密钥文件路径失败: {e}")
            return f"{self.account_keyfile_path}/{self.account_keyfile}"
    
    def get_contract_info_file_path(self) -> str:
        """获取合约信息文件路径"""
        return self.get_path(self.contract_info_file)
    
    def validate_config(self) -> bool:
        """
        验证配置的有效性
        
        Returns:
            配置是否有效
        """
        try:
            # 检查必要的配置项
            if not self.channel_host:
                logger.error("节点主机地址不能为空")
                return False
            
            if self.channel_port <= 0 or self.channel_port > 65535:
                logger.error("节点端口必须在1-65535之间")
                return False
            
            if self.group_id <= 0:
                logger.error("群组ID必须大于0")
                return False
            
            if not self.contract_name:
                logger.error("合约名称不能为空")
                return False
            
            # 检查加密类型
            if self.crypto_type.upper() not in ["ECDSA", "GM"]:
                logger.error("加密类型必须是 ECDSA 或 GM")
                return False
            
            # 验证关键资源文件存在性
            try:
                self.get_abi_content()
                logger.info(f"ABI资源文件验证成功: {self.abi_filename}")
            except Exception as e:
                logger.error(f"ABI资源文件不存在: {self.abi_filename}, 错误: {e}")
                return False
            
            # 验证可选资源文件存在性
            try:
                self.get_bin_content()
                logger.info(f"BIN资源文件验证成功: {self.bin_filename}")
            except Exception as e:
                logger.warning(f"BIN资源文件不存在: {self.bin_filename}, 错误: {e}")
            
            return True
            
        except Exception as e:
            logger.error(f"配置验证失败: {e}")
            return False
    
    def __str__(self) -> str:
        """字符串表示"""
        return f"BlockchainConfig(peers={self.peers}, group_id={self.group_id}, contract={self.contract_name})"


# 全局配置实例
blockchain_config = BlockchainConfig()